using Microsoft.Extensions.Caching.Memory;

namespace Beauty.Net.Cache.Core;

/// <summary>
/// 内存缓存服务实现
/// </summary>
public class MemoryCacheService : ICacheService
{
    private readonly IMemoryCache _memoryCache;

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="memoryCache">内存缓存</param>
    public MemoryCacheService(IMemoryCache memoryCache)
    {
        _memoryCache = memoryCache;
    }

    /// <summary>
    /// 获取缓存
    /// </summary>
    /// <typeparam name="T">缓存数据类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <returns>缓存值</returns>
    public T? Get<T>(string key)
    {
        _memoryCache.TryGetValue(key, out T? value);
        return value;
    }

    /// <summary>
    /// 异步获取缓存
    /// </summary>
    /// <typeparam name="T">缓存数据类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <returns>缓存值</returns>
    public Task<T?> GetAsync<T>(string key)
    {
        // 内存缓存操作本身是同步的，这里为了保持接口一致性提供异步方法
        return Task.FromResult(Get<T>(key));
    }

    /// <summary>
    /// 设置缓存
    /// </summary>
    /// <typeparam name="T">缓存数据类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <param name="value">缓存值</param>
    /// <param name="expiry">过期时间</param>
    public void Set<T>(string key, T value, TimeSpan? expiry = null)
    {
        if (expiry.HasValue)
        {
            _memoryCache.Set(key, value, expiry.Value);
        }
        else
        {
            _memoryCache.Set(key, value);
        }
    }

    /// <summary>
    /// 异步设置缓存
    /// </summary>
    /// <typeparam name="T">缓存数据类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <param name="value">缓存值</param>
    /// <param name="expiry">过期时间</param>
    public Task SetAsync<T>(string key, T value, TimeSpan? expiry = null)
    {
        Set(key, value, expiry);
        return Task.CompletedTask;
    }

    /// <summary>
    /// 删除缓存
    /// </summary>
    /// <param name="key">缓存键</param>
    public void Remove(string key)
    {
        _memoryCache.Remove(key);
    }

    /// <summary>
    /// 异步删除缓存
    /// </summary>
    /// <param name="key">缓存键</param>
    public Task RemoveAsync(string key)
    {
        Remove(key);
        return Task.CompletedTask;
    }

    /// <summary>
    /// 批量删除缓存
    /// </summary>
    /// <param name="keys">缓存键集合</param>
    public void RemoveRange(IEnumerable<string> keys)
    {
        foreach (var key in keys)
        {
            Remove(key);
        }
    }

    /// <summary>
    /// 异步批量删除缓存
    /// </summary>
    /// <param name="keys">缓存键集合</param>
    public Task RemoveRangeAsync(IEnumerable<string> keys)
    {
        RemoveRange(keys);
        return Task.CompletedTask;
    }

    /// <summary>
    /// 判断缓存是否存在
    /// </summary>
    /// <param name="key">缓存键</param>
    /// <returns>是否存在</returns>
    public bool Exists(string key)
    {
        return _memoryCache.TryGetValue(key, out _);
    }

    /// <summary>
    /// 异步判断缓存是否存在
    /// </summary>
    /// <param name="key">缓存键</param>
    /// <returns>是否存在</returns>
    public Task<bool> ExistsAsync(string key)
    {
        return Task.FromResult(Exists(key));
    }

    /// <summary>
    /// 获取缓存并设置缓存（如果不存在）
    /// </summary>
    /// <typeparam name="T">缓存数据类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <param name="factory">缓存值工厂</param>
    /// <param name="expiry">过期时间</param>
    /// <returns>缓存值</returns>
    public T GetOrSet<T>(string key, Func<T> factory, TimeSpan? expiry = null)
    {
        if (expiry.HasValue)
        {
            return _memoryCache.GetOrCreate(key, entry =>
            {
                entry.SlidingExpiration = expiry;
                return factory();
            });
        }
        
        return _memoryCache.GetOrCreate(key, _ => factory());
    }

    /// <summary>
    /// 异步获取缓存并设置缓存（如果不存在）
    /// </summary>
    /// <typeparam name="T">缓存数据类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <param name="factory">缓存值工厂</param>
    /// <param name="expiry">过期时间</param>
    /// <returns>缓存值</returns>
    public async Task<T> GetOrSetAsync<T>(string key, Func<Task<T>> factory, TimeSpan? expiry = null)
    {
        if (expiry.HasValue)
        {
            return await _memoryCache.GetOrCreateAsync(key, async entry =>
            {
                entry.SlidingExpiration = expiry;
                var result = await factory();
                return result;
            });
        }
        
        return await _memoryCache.GetOrCreateAsync(key, async _ =>
        {
            var result = await factory();
            return result;
        });
    }
}